#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <cx_facerecog.h>
#include <cx_debug.h>
#include <dirent.h>
#include "if_v.h"

#define TAG "cxfrecog"

#ifndef CX_MAX_USER_IDS
#define CX_MAX_USER_IDS		100
#endif

#define USER_INFO_FILENAME	"uf"

typedef struct {
	int valid;			// is a valid user info
	int is_admin;
	char user_name[CX_FR_MAX_USRNAME_LEN];
} user_info_t;

typedef struct {
	user_info_t infos[CX_MAX_USER_IDS];
} user_list_t;

typedef struct {
    void (* state_cb)(cx_fr_state_t *state);
    void (* register_cb)(cx_fr_register_result_t *result);
    void (* verify_cb)(cx_fr_verify_result_t *result);

	char *root_path;
	char *userinfo_save_path;
	user_list_t *user_infos;
} cx_facerecog_priv_t;

static cx_facerecog_priv_t *g_cx_fr_priv;

extern uint32_t aie_ir_detect_enable;
void userCmdRegisterCb(void *pBuff);
void userCmdRecognizeCb(void *pBuff);
void userCmdRecogDoubleIRCb(void *pBuff);
void userCmdRegDoubleIRCb(void *pBuff);
void userCmdRegTofCb(void *pBuff);
void userCmdRecogTofCb(void *pBuff);
void userCmdVisDetectCb(void *pBuff);
void userCmdCalibDoubleIRCb(void *pBuff);

extern void registerResultAck(int result,uint16_t id);
extern bool if_vInitial_iva_sl3d();
extern bool if_vInitial_iva_aie();
uint8_t *ir_pic_addr;
UserCmdCB userCmdArray[16] =
{
    {&userCmdRegisterCb,"register cmd from user"},   
    {&userCmdRecognizeCb,"recognize cmd from user"},  
    {&userCmdRecogDoubleIRCb,"recognize double IR cmd from user"},  
    {&userCmdRegDoubleIRCb,"register double IR cmd from user"}, 
	{&userCmdRegTofCb,"register Tof cmd from user"}, 
	{&userCmdRecogTofCb,"recognize Tof cmd from user"},  
	{&userCmdVisDetectCb,"vis detect cmd from user"},
	{&userCmdCalibDoubleIRCb,"calibration double IR cmd from user"},
    {0, ""}                                  
};


void userCmdRegisterCb(void *pBuff)
{
	//printf("%s result %d, id 0x%x\n",__func__, ((CmdRegisterCBinfo_S *)pBuff)->result, ((CmdRegisterCBinfo_S *)pBuff)->id );

	CmdRegisterCBinfo_S * info = (CmdRegisterCBinfo_S *)pBuff;

	if (g_cx_fr_priv && g_cx_fr_priv->register_cb) {
			cx_fr_register_result_t result;

			result.success = info->result == 0 ? 1 : 0;
			result.user_id = info->id;

			g_cx_fr_priv->register_cb(&result);
	}
}
extern void recognizeResultAck(int result,uint16_t id);
void userCmdRecognizeCb(void *pBuff)
{
	CmdRecognizeCBinfo_S *info = (CmdRecognizeCBinfo_S *)pBuff;

	if (g_cx_fr_priv && g_cx_fr_priv->verify_cb) {
		cx_fr_verify_result_t result;

		result.result = info->result;
		result.user_id = info->id;

		g_cx_fr_priv->verify_cb(&result);
	}
	if(2 == ((CmdRecognizeCBinfo_S *)pBuff)->flag)  //flag=2:识别结果回调
	{
	    printf("%s result %d, id 0x%x, pose %f %f\n",__func__, ((CmdRecognizeCBinfo_S *)pBuff)->result,((CmdRecognizeCBinfo_S *)pBuff)->id,((CmdRecognizeCBinfo_S *)pBuff)->pose[0],((CmdRecognizeCBinfo_S *)pBuff)->pose[1] );
	    recognizeResultAck(((CmdRecognizeCBinfo_S *)pBuff)->result, ((CmdRecognizeCBinfo_S *)pBuff)->id);
	}
	else if(1 == ((CmdRecognizeCBinfo_S *)pBuff)->flag)  //flag=1:注册结果回调
	{
	    registerResultAck(((CmdRecognizeCBinfo_S *)pBuff)->result, ((CmdRecognizeCBinfo_S *)pBuff)->id);
	}
}

void userCmdRecogDoubleIRCb(void *pBuff)
{
}

void userCmdRegDoubleIRCb(void *pBuff)
{
}

void userCmdRegTofCb(void *pBuff)
{
}

void userCmdRecogTofCb(void *pBuff)
{
}

void userCmdVisDetectCb(void *pBuff)
{
}

extern g_variable_control_t g_variable_cfg;
extern uint32_t g_usbPicSaveEnable;
void userCmdCalibDoubleIRCb(void *pBuff)
{
	CmdRecognizeCBinfo_S *userInfo = pBuff;
	ifv_msg_pkg_t msg;

	if(0 == userInfo->result)
		printf("%s %d calib ok! \n",__func__, userInfo->result);
	else
		printf("%s %d calib err! \n",__func__, userInfo->result);

	if(1 == g_variable_cfg.g_read_pic_by_usb_flag){
		g_variable_cfg.g_calibration_flag = 0;
		g_variable_cfg.g_calib_estimate_flag = 0;
		return;
	}
		
	if(true == g_usbPicSaveEnable){
		// extern uint8_t *double_ir_buf0;
		// extern uint8_t *double_ir_buf1;
		uint32_t width=0;
		uint32_t height=0;
		isp_res_map(g_ifv_flow_cfg.enResIR, &width, &height);
#if CONFIG_DEPTH_DOUBLE_IR
		usb_save_calib_double_ir_depth_pic(((CmdRecognizeCBinfo_S *)pBuff)->flag, ((CmdRecognizeCBinfo_S *)pBuff)->result, double_ir_buf0, width*height, double_ir_buf1, width*height, g_2ir_depth_buf, width*height*2);
#endif	
	}

	if(g_variable_cfg.g_calibration_loop_count <= IFV_DOUBLE_IR_CALIB_LOOP_COUNT){
		if(g_variable_cfg.g_calibration_flag){
			g_variable_cfg.g_calibration_flag = 0;
			msg.cmd = APP2IFV_CALIB_ESTIMATE_CMD; 
			ifv_post_msg(msg);
		}else if((g_variable_cfg.g_calib_estimate_flag) && (g_variable_cfg.g_calibration_loop_count < IFV_DOUBLE_IR_CALIB_LOOP_COUNT)){
			g_variable_cfg.g_calibration_loop_count++;

			g_variable_cfg.g_calib_estimate_flag = 0;
			msg.cmd = APP2IFV_CALIBRATION_CMD;
			ifv_post_msg(msg);
		}
	}
}

static int load_user_info(char *file_path)
{
	CHECK_PARAM(file_path && g_cx_fr_priv, -1);

	FILE *f = fopen(file_path, "r");
	if (!f) {
		LOGW(TAG, "%d file open failed", __LINE__);
		return -1;
	}

	size_t ret = fread(g_cx_fr_priv->user_infos, 1, sizeof(user_list_t), f);

	fclose(f);

	return ret == sizeof(user_list_t) ? 0 : -1;
}

static int save_user_info(char *file_path)
{
	CHECK_PARAM(file_path && g_cx_fr_priv, -1);

	// TODO: CRC generate and backup file
	FILE *f = fopen(g_cx_fr_priv->userinfo_save_path, "w");
	if (!f) {
		LOGE(TAG, "%d file open failed", __LINE__);
		return -1;
	}

	size_t ret = fwrite(g_cx_fr_priv->user_infos, sizeof(user_list_t), 1, f);
	fclose(f);

	return ret > 0 ? 0 : -1;
}


int cx_facerecog_init(
    cx_fr_config_t *settings,
    void (* user_state_cb)(cx_fr_state_t *state),
    void (* user_register_cb)(cx_fr_register_result_t *result),
    void (* user_verify_cb)(cx_fr_verify_result_t *result)
)
{
	data_flow_func_control_t *flow_ctrl;

	CHECK_PARAM(settings && settings->alg_spec, -1);

	g_cx_fr_priv = (cx_facerecog_priv_t *)calloc(1, sizeof(cx_facerecog_priv_t));
	CHECK_RET_WITH_RET(g_cx_fr_priv, -1);

	g_cx_fr_priv->user_infos = (user_list_t *)calloc(1, sizeof(user_list_t));
	CHECK_RET_WITH_RET(g_cx_fr_priv->user_infos, -1);

	g_cx_fr_priv->root_path = settings->info_save_path;
	if (!g_cx_fr_priv->root_path || strlen(g_cx_fr_priv->root_path) < 1) {
		LOGE(TAG, "user info path error");
		return -1;
	}

	int path_len = strlen(g_cx_fr_priv->root_path) + strlen(USER_INFO_FILENAME) + 2;
	g_cx_fr_priv->userinfo_save_path = (char *)calloc(1, path_len);
	snprintf(g_cx_fr_priv->userinfo_save_path, path_len, "%s/%s", g_cx_fr_priv->root_path, USER_INFO_FILENAME);
	
    if (access(g_cx_fr_priv->root_path, 0) != 0) {
        mkdir(g_cx_fr_priv->root_path, 755);
        LOGD(TAG, "create dir %s\n", g_cx_fr_priv->root_path);
    }

	load_user_info(g_cx_fr_priv->userinfo_save_path);

	g_cx_fr_priv->state_cb = user_state_cb;
	g_cx_fr_priv->register_cb = user_register_cb;
	g_cx_fr_priv->verify_cb = user_verify_cb;
	flow_ctrl = (data_flow_func_control_t *)settings->alg_spec;

	if ((flow_ctrl->source_ir_right) && (flow_ctrl->depth_doubleIr_enable)){
		if_vCalibration_initial();
	}

	if (flow_ctrl->source_vis)
	{
		if_vInitial_vis();
	}
	if_vInitial((UserCmdCB *)&userCmdArray, *flow_ctrl);

	cx_facerecog_user_verify(0);

	if_vInitial_iva_aie();	
	if_vInitial_iva_sl3d();
	setIDStartNo(0);
	if_vInitial_ff();

	return 0;
}

int cx_facerecog_deinit(void)
{
	return 0;
}

int cx_facerecog_user_enroll(int face_dir, int timeout_s)
{
	ifv_msg_pkg_t msg;
	msg.cmd = APP2IFV_REGISTER_CMD;
	ifv_post_msg(msg);
	
	return 0;
}

int cx_facerecog_add_user_info(int user_id, cx_fr_user_info_t *info)
{
	CHECK_PARAM(info && g_cx_fr_priv && g_cx_fr_priv->userinfo_save_path, -1);

	if (user_id >= CX_MAX_USER_IDS) {
		LOGE(TAG, "user id too big");
		return -1;
	}

	g_cx_fr_priv->user_infos->infos[user_id].valid = 1;
	g_cx_fr_priv->user_infos->infos[user_id].is_admin = info->is_admin;
	memcpy(g_cx_fr_priv->user_infos->infos[user_id].user_name, info->user_name, CX_FR_MAX_USRNAME_LEN);

	return save_user_info(g_cx_fr_priv->userinfo_save_path);
}

int cx_facerecog_get_user_info(int user_id, cx_fr_user_info_t *info)
{
	CHECK_PARAM(info && g_cx_fr_priv && g_cx_fr_priv->user_infos && user_id < CX_MAX_USER_IDS, -1);
	
	if (g_cx_fr_priv->user_infos->infos[user_id].valid) {
		info->is_admin = g_cx_fr_priv->user_infos->infos[user_id].is_admin;
		memcpy(info->user_name, g_cx_fr_priv->user_infos->infos[user_id].user_name, CX_FR_MAX_USRNAME_LEN);
	} else {
		LOGW(TAG, "no user_id=%d info", user_id);
		return -1;
	}

	return 0;
}

int cx_facerecog_get_all_user_id(int max_user_count, int user_ids[])
{
	CHECK_PARAM(user_ids && g_cx_fr_priv && g_cx_fr_priv->user_infos &&
				max_user_count > 0, -1);

	int cnt = 0;

	for (int i = 0; i < CX_MAX_USER_IDS && cnt < max_user_count; i++) {
		if (g_cx_fr_priv->user_infos->infos[i].valid) {
			user_ids[cnt++] = i;
		}
	}

	return cnt;
}

int cx_facerecog_user_verify(int timeout)
{
	ifv_msg_pkg_t msg;

	aie_ir_detect_enable = 0;
	msg.cmd = APP2IFV_RECOGNIZE_CMD;
	ifv_post_msg(msg);

	return 0;
}

int cx_facerecog_user_delete(cx_fr_user_type_e type, int user_id)
{
	CHECK_PARAM(g_cx_fr_priv && g_cx_fr_priv->userinfo_save_path && user_id < CX_MAX_USER_IDS, -1);

	switch (type) {
		case CX_USER_DEL_SINGLE:
			if (!g_cx_fr_priv->user_infos->infos[user_id].valid) {
				LOGW(TAG, "user id=%d not exist", user_id);
				return -1;
			}

			LOGD(TAG, "delete user id=%d\n", user_id);
			tsm_ff_delete(user_id);
			g_cx_fr_priv->user_infos->infos[user_id].valid = 0;
			return save_user_info(g_cx_fr_priv->userinfo_save_path);
			break;

		case CX_USER_DEL_ALL:

			LOGD(TAG, "delete user all\n");
			tsm_ff_clear();
			memset(g_cx_fr_priv->user_infos, 0, sizeof(user_list_t));
			unlink(g_cx_fr_priv->userinfo_save_path);
			break;

		default:
			break;
	}

	return 0;
}


